//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<observer.cpp>
///	@path	~/src/lib/utilities/
///	@date	2007/11/10
///	@desc	Data observer.

#include "config/config.h"

#include "lib/utilities/helper.h"
#include "lib/utilities/observer.h"
#include "lib/system/lock.h"

#include <map>
#include <typeinfo>

namespace xeres {

	// AddObserver
	void AddObserver( const WString& fulldesc , Observer * observer )
	{
		size_t desc_pos = fulldesc.find_first_of( ':' );
		XS_ASSERT( desc_pos!= WString::npos );
		WString path = fulldesc.substr( 0 , desc_pos );
		WString desc = fulldesc.substr( desc_pos + 1 );
		for( size_t i = 0 , total = path.size() ; i < total ; ++i ) if( path[i] =='\t' ) path[i] = ' ';
		size_t path_pos = path.find_last_not_of( ' ' );
		if( path_pos != WString::npos ) path = path.substr( 0 , path_pos );
		desc_pos = fulldesc.find_first_not_of( ' ' );
		XS_ASSERT( desc_pos!= WString::npos );
		if( desc_pos != 0 ) desc = desc.substr( desc_pos );
		observer->SetDesc( desc );
		Observer::root()->AddToChain( path , observer );
	}

	// root
	Observer * Observer::root( void )
	{
		static boost::shared_ptr<Observer> s_ObserverRootInstance = boost::shared_ptr<Observer>( new DirectoryObserver );
		return s_ObserverRootInstance.get();
	}

	// Observer
	Observer::Observer( ObserverType accessType )
		: m_accessType( accessType )
	{
	}

	// ~Observer
	Observer::~Observer( void )
	{
	}

	// AddChild
	bool Observer::AddChild( const WString& name , Observer * child )
	{
		return false;
	}

	// GetChild
	Observer * Observer::GetChild( const WString& name )
	{
		return NULL;
	}

	// GetChildByPath
	Observer * Observer::GetChildByPath( const WString& path )
	{
		return NULL;
	}

	// AddToChain
	void Observer::AddToChain( const WString& path , Observer * node )
	{
		StringSeq seq;
		break_string( path , '/' , seq );
		Observer * parent = this , *o , *dir;

		for( size_t i = 0 , total = seq.size() - 1 ; i < total ; ++i )
		{
			o = parent->GetChild( seq[i] );
			if( !o )
			{
				dir = new DirectoryObserver;
				parent->AddChild( seq[i] , dir );
				parent = dir;
			}
			else
			{
				parent = o;
			}
		}
		if( parent->GetChild( seq.back() ) )
		{
			TRACE_ERROR( L"Observer::AddToChain: Observer '%s' is defined." , path.c_str() );
			return;
		}
		parent->AddChild( seq.back() , node );
	}

	// VisitChildren
	bool Observer::VisitChildren( const WString& path , Observation observation )
	{
		return false;
	}
	
	// DirectoryObserver
	DirectoryObserver::DirectoryObserver( void )
		: Observer( OBSERVER_DIR )
	{
	}

	// ~DirectoryObserver
	DirectoryObserver::~DirectoryObserver( void )
	{
	}

	// AddChild
	bool DirectoryObserver::AddChild( const WString& name , Observer * child )
	{
		for( size_t i = 0 , total = m_directory.size() ; i < total ; ++i )
		{
			if( m_directory[i].label == name )
			{
				TRACE_ERROR( L"DirectoryObserver::AddChild: Failed to add child '%s', child existed." ,
					name.c_str() );
				return false;
			}
		}
		_DirData data;
		data.label = name;
		data.observer = boost::shared_ptr<Observer>( child );
		m_directory.push_back( data );
		return true;
	}

	// GetChild
	Observer * DirectoryObserver::GetChild( const WString& name )
	{
		for( size_t i = 0 , total = m_directory.size() ; i < total ; ++i )
		{
			if( m_directory[i].label == name )
			{
				return m_directory[i].observer.get();
			}
		}
		return NULL;
	}

	// VisitChildren
	bool DirectoryObserver::VisitChildren( const WString& path , Observation observation )
	{
		// no path specified, visit all
		if( path.empty() )
		{
			for( size_t i = 0 , total = m_directory.size() ; i < total ; ++i )
			{
				_DirData& data = m_directory[i];
				if( !observation( data.observer->GetAccessType() , data.observer.get() , data.label ) )
					return false;

				if( data.observer->GetAccessType() == OBSERVER_DIR )
				{
					if( !data.observer->VisitChildren( path , observation ) )
						return false;
					if( !observation( OBSERVER_last , NULL , L"" ) )
						return false;
				}
			}
			return true;
		}

		// path specified, visit children in seq
		StringSeq seq;
		break_string( path , '/' , seq );
		Observer * parent = this;
		Observer * o;

		for( size_t i = 0 , total = seq.size() - 1 ; i < total ; ++i )
		{
			o = parent->GetChild( seq[i] );
			if( !o )
			{
				TRACE_ERROR( L"DirectoryObserver::VisitChildren: Path '%s' not found." , path.c_str() );
				return false;
			}
			else
			{
				parent = o;
				if( o->GetAccessType() != OBSERVER_DIR )
				{
					TRACE_ERROR( L"DirectoryObserver::VisitChildren: Path '%s' in '%s' is not a directory node." , seq[i].c_str() , path.c_str() );
					return false;
				}
			}
		}
		if( seq.back() == _S("*") )
		{
			if( !parent->VisitChildren( _S("") , observation ) )
				return false;
		}
		else
		{
			o = parent->GetChild( seq.back() );
			if( !o )
			{
				TRACE_ERROR( L"DirectoryObserver::VisitChildren: Path '%s' not found." , path.c_str() );
				return false;
			}
			if( !observation( o->GetAccessType() , o , seq.back() ) )
				return false;
		}
		return true;
	}
	// GetChildByPath
	Observer * DirectoryObserver::GetChildByPath( const WString& path )
	{
		// no path specified, visit all
		if( path.empty() )
		{
			return NULL;
		}

		// path specified, visit children in seq
		StringSeq seq;
		break_string( path , '/' , seq );
		Observer * parent = this;
		Observer * o;

		for( size_t i = 0 , total = seq.size() - 1 ; i < total ; ++i )
		{
			o = parent->GetChild( seq[i] );
			if( !o )
			{
				TRACE_ERROR( L"DirectoryObserver::GetChildByPath: Path '%s' not found." , path.c_str() );
				return NULL;
			}
			else
			{
				parent = o;
				if( !o->HasChild() )
				{
					TRACE_ERROR( L"DirectoryObserver::GetChildByPath: Path '%s' in '%s' has no node." , seq[i].c_str() , path.c_str() );
					return NULL;
				}
			}
		}
		o = parent->GetChild( seq.back() );
		if( !o )
		{
			TRACE_ERROR( L"DirectoryObserver::GetChildByPath: Path '%s' not found." , path.c_str() );
			return NULL;
		}
		return o;
	}
	// GetValue
	boost::any DirectoryObserver::GetValue( void )
	{
		return boost::any( WString( _S("") ) );
	}

	// SetValue
	bool DirectoryObserver::SetValue( const boost::any& value )
	{
		FATAL_ABORT( _S("Unexpected SetValue") );
		return false;
	}

	ThreadLock s_counterLock;
	std::deque< CounterObserver * >	CounterObserver::s_counters;
	// CounterObserver
	CounterObserver::CounterObserver( void )
		: DirectoryObserver() , m_counter( 0 ) , m_last( 0 )
	{
		LockGuard<ThreadLock> g( s_counterLock );
		s_counters.push_back( this );
	}
	// ~CounterObserver
	CounterObserver::~CounterObserver( void )
	{
	}
	// NextFrame
	void CounterObserver::NextFrame( void )
	{
		LockGuard<ThreadLock> g( s_counterLock );
		for( size_t i = 0 , total = s_counters.size() ; i < total ; ++i )
		{
			s_counters[i]->m_last = s_counters[i]->m_counter;
			s_counters[i]->m_counter = 0;
		}
	}
	// GetAccessType
	ObserverType CounterObserver::GetAccessType( void ) const
	{
		if( HasChildren() )
			return OBSERVER_DIR;
		else
			return OBSERVER_READONLY;
	}
	// GetValue
	boost::any CounterObserver::GetValue( void )
	{
		double sec = m_last.ToMilliSecond();
		return boost::any( sec );
	}
	// CounterHolder
	CounterHolder::CounterHolder( const WString& path )
		: m_counter(0)
	{
		// Guarantees that root node must be defined.
		CounterRoot();
		XS_ASSERT( !path.empty() );

		// to see if it's path or name
		if( path[0] == '+' )
		{
			WString realpath = path.substr( 1 );
			// path to join counter
			Observer * o = Observer::root()->GetChildByPath( realpath );
			if( !o )
			{
				m_observer = new CounterObserver();
				Observer::root()->AddToChain( realpath , m_observer );
			}
			else
			{
				m_observer = dynamic_cast<CounterObserver*>( o );
				XS_ASSERT( m_observer );
			}
		}
		else if( path[0] == '*' )
		{
			WString realpath = path.substr( 1 );
			XS_ASSERT( !Observer::root()->GetChildByPath( realpath ) );
			// define new counter
			m_observer = new CounterObserver();
			Observer::root()->AddToChain( realpath , m_observer );
		}
		else
		{
			WString realpath = path;
			CounterHolder * current = Current(NULL);

			// define new counter
			m_observer = new CounterObserver();

			if( current == NULL )
			{
				realpath = L"counter/" + path;
				Observer::root()->AddToChain( realpath , m_observer );
			}
			else
			{
				current->m_observer->AddChild( path , m_observer );
			}
		}
	}
	// CounterHolder
	CounterHolder::CounterHolder( void )
	{
		m_observer = new CounterObserver();
		Observer::root()->AddToChain( _S("counter") , m_observer );
	}
	// ~CounterHolder
	CounterHolder::~CounterHolder( void )
	{
	}
	// Start
	void CounterHolder::Start( void )
	{
		m_parent = Current( this );
		m_counter = Counter::Now();
	}
	// End
	void CounterHolder::End( void )
	{
		Counter now = Counter::Now();
		m_observer->m_counter = m_observer->m_counter + ( now - m_counter );
		m_counter = 0;
		XS_VERIFY( Current( m_parent ) == this );
	}
	namespace
	{
		struct ThreadLocalHolder
		{
		public:

			/// \instance
			static ThreadLocalHolder& instance( void )
			{
				static ThreadLocalHolder s_threadLocalInstance;
				return s_threadLocalInstance;
			}

			/// \brief	Get current holder.
			CounterHolder * Get( DWORD threadid )
			{
				std::map<DWORD,CounterHolder*>::iterator it = m_storage.find(threadid);
				if( it == m_storage.end() )
				{
					m_storage[threadid] = NULL;
					return NULL;
				}
				return it->second;
			}

			/// \brief	Set current holder.
			CounterHolder * Set( DWORD threadid , CounterHolder * val )
			{
				std::map<DWORD,CounterHolder*>::iterator it = m_storage.find(threadid);
				if( it == m_storage.end() )
				{
					m_storage[threadid] = val;
					return NULL;
				}
				CounterHolder * retval = it->second;
				it->second = val;
				return retval;
			}

		private:

			std::map<DWORD,CounterHolder*> m_storage;
		};
	}
	// Current
	CounterHolder * CounterHolder::Current( CounterHolder * next_current )
	{
		DWORD id = ::GetCurrentThreadId();
		ThreadLocalHolder& tls = ThreadLocalHolder::instance();
		if( next_current )
			return tls.Set( id , next_current );
		return tls.Get( id );
	}
	// CounterRoot
	CounterHolder& CounterHolder::CounterRoot( void )
	{
		static CounterHolder s_rootHolder;
		return s_rootHolder;
	}

} // namespace xeres
